package com.authsphere.plugin.base.scanner;

import com.authsphere.common.annotation.CaptchaField;
import com.authsphere.common.annotation.CryptField;
import com.authsphere.common.annotation.EnabledAuthSphereField;
import com.authsphere.common.annotation.NamedField;
import com.authsphere.plugin.base.cache.AnnotationDataCache;
import com.authsphere.plugin.base.entity.AnnotationFieldBean;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.ReflectionUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;

/**
 * @program: AuthSphere
 * @description: scan 所有携带 对应注解的 实体类中的对应的Field（目前值支持Field），并放入缓存中
 * @author: YuKai Fan
 * @create: 2025/3/28 21:53
 **/
public class AnnotationScanner implements ApplicationListener<ContextRefreshedEvent> {

    private final List<Class<? extends Annotation>> annotationList = Lists.newArrayList(
            CryptField.class,
            NamedField.class,
            CaptchaField.class
    );

    public void scanAnnotations() {
        String basePackage = "com.authsphere";
        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
        scanner.addIncludeFilter(new AnnotationTypeFilter(EnabledAuthSphereField.class));
        Set<BeanDefinition> components = scanner.findCandidateComponents(basePackage);
        for (BeanDefinition component : components) {
            try {
                Class<?> clazz = Class.forName(component.getBeanClassName());
                for (Field annotatedField : clazz.getDeclaredFields()) {
                    for (Class<? extends Annotation> annotationClass : annotationList) {
                        if (annotatedField.isAnnotationPresent(annotationClass)) {
                            Annotation annotation = annotatedField.getAnnotation(annotationClass);
                            String getterMethodName = "get" + capitalize(annotatedField.getName());
                            String setterMethodName = "set" + capitalize(annotatedField.getName());
                            Method getterMethod = ReflectionUtils.findMethod(clazz, getterMethodName);
                            if (getterMethod == null) {
                                throw new RuntimeException("No getter method found for field " + annotatedField.getName() + " in class " + clazz.getName());
                            }
                            Method setterMethod = ReflectionUtils.findMethod(clazz, setterMethodName, String.class);
                            if (setterMethod == null) {
                                throw new RuntimeException("No setter method found for field " + annotatedField.getName() + " in class " + clazz.getName());
                            }
                            AnnotationDataCache.register(clazz, annotationClass, new AnnotationFieldBean(annotation, annotatedField, getterMethod, setterMethod), true);
                        }
                    }
                }
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
    }


    private String capitalize(String name) {
        if (StringUtils.isBlank(name)) {
            return name;
        }
        return name.substring(0, 1).toUpperCase() + name.substring(1);
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        scanAnnotations();
    }
}
